﻿namespace Assembler_Compiler
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.IO;

    public class Compiler
    {
        // compiles code, creates a com file and runs it within a bat file.
        public void Compile(string filePath)
        {
            // Cleans error stack
            ErrorList.Clean();

            OPTAB.Initialize();
            SYMTAB.Initialize();

            List<Line> lines = new List<Line>();
            String[] stringsOfCode = File.ReadAllText(filePath).Replace('\t',' ').Split('\n');
            List<bool> Builded = new List<bool>();
            Int16 offset = 256;
            int lineNumb = 0;
            foreach (String s in stringsOfCode)
            {
                ++lineNumb;
                try
                {
                    lines.Add(new Line(s, offset));
                    offset += lines[lines.Count - 1].LineByteLenght;
                    Builded.Add(true);
                }
                catch (Exception ex)
                {
                    ErrorList.AddError(new Error(){Line = lineNumb,Message = ex.Message,Type = ErrorType.Build});
                    Builded.Add(false);
                }
            }

            // analysing all lines and pushing their machine code eqvivalents into array
            int i = 0;
            byte[] binArray = new byte[offset - 256];
            //foreach (Line line in lines)
            for (int ii = 0, j = 0; ii < Builded.Count && j < lines.Count; ++ii )
            {
                if(!Builded[ii])
                    continue;
                Line line = lines[j++];
                try
                {
                    if (line.IsComment == false)
                    {
                        byte[] machineCode = line.GetMachineCode();
                        if (machineCode != null)
                            for (int q = 0; q < machineCode.Length; q++)
                            {
                                binArray[i++] = machineCode[q];
                            }

                    }
                }
                catch (Exception ex)
                {
                    ErrorList.AddError(new Error(){Line = ii, Message = ex.Message, Type = ErrorType.Compile});
                }
            }

            // Don`t run program if there are build or compile errors.
            if(ErrorList.Count != 0)
                return;

            // creating com file if necessary and writing a binary there
            string comFilePath = Path.ChangeExtension(filePath, "com");
            BinaryWriter writer = null;
            try
            {
                writer = new BinaryWriter(File.Open(comFilePath, FileMode.CreateNew, FileAccess.Write));
            }
            catch (IOException)
            {
                File.Delete(comFilePath);
                writer = new BinaryWriter(File.Open(comFilePath, FileMode.CreateNew, FileAccess.Write));
            }

            try
            {

                writer.Write(binArray);
                writer.Close();

                this.CreateAndStartBat(comFilePath);
            }
            catch (Exception ex)
            {
                ErrorList.AddError(new Error(){Line = -1, Message = ex.Message, Type = ErrorType.Run});
            }
        }

        private void CreateAndStartBat(string comFilePath)
        {
            var file = File.Create(Path.ChangeExtension(comFilePath, "bat"));
            StreamWriter writer = new StreamWriter(file);
            writer.Write("@echo off\nCLS\n"+ comFilePath +"\nPAUSE\nEXIT");
            writer.Close();

            System.Diagnostics.Process.Start(Path.ChangeExtension(comFilePath, "bat"));
        }
    }
}
